perm filename FTDOC[PAT,LMM] blob
sn#092638 filedate 1974-03-16 generic text, type T, neo UTF8
USER DEFINED DATA TYPES
We have completed the initial implementation of user defined data
types and have integrated this package with the RECORD package of
CLISP. This allows the user to specify data structures containing
only what he needs in a more compact form, and with faster access
time, than if list structures were used. The storage allocation and
garbage collection of user defined data types is the same as for any
regular LISP data type, and is therefore just as efficient. This
data definition facility is not completely general; for example, not
all the data types that currently exist in LISP are user defineable.
However, it provides a significant increase in the data definition
capabilities of LISP.
The scheme used provides the ability to define fixed length data
types composed of fixed length components. Each component may be a
pointer, an integer, a floating point number, or some number of
bits.
To declare a DATATYPE, enter the declaration:
(DATATYPE name fields . defaults & subfields)
just like any other RECORD declaration.
For DATATYPE record declarations, "fields" is a list of the fields
contained in the data type. Each field is of the form:
field-name the field is a pointer (i.e. you can store
(field-name POINTER) any arbitrary LISP datum in this field)
(field-name BITS n) the field is an n-bit unsigned integer.
(field-name BIT) equivalent to ...BITS 1)
(field-name BYTE) equivalent to ...BITS 7)
(field-name INTEGER) the field is a full word signed integer.
(field-name FLOATING) the field is a floating point number.
(field-name HALFWORD) the field is a half word signed integer.
{INT=FIXP=INTEGER, REAL=FLOAT=FLOATING, ATOM=PTR=POINTER,
HALF=HALFWORD are all allowable synonyms }.
DATATYPEs differ from other types of records in that the garbage
collector must be made aware of them explicitly, separate pages of
the LISP address space must be allocated for data of this type;
further, as in TYPERECORD's, there must be a unique identifier (atom)
which is common among all data types.
Thus, the "name" portion of a DATATYPE declaration may be
(RECORDNAME DATATYPENAME)
where RECORDNAME is the name used in CREATE and TYPE? expressions as
well as for identification in RECORDS prettycoms; while DATATYPENAME
must be a unique identifier which is common to local and global
declarations which expect to access datum of this type. Normally,
"name" is both the RECORDNAME and the DATATYPENAME.
DATATYPE declarations can have subfields and default values, and can
BE sub-declarations to other record declarations. Currently no error
checking is done to insure that numeric fields are not given
subfields (other than ACCESSFN subfields).
ADDITIONAL NOTES:
An additional feature of the RECORD package is the TYPE? statement:
an expression of the form
(TYPE? recordname expr)
will be translated to the appropriate expression to check if the
value of "expr" is of the type "recordname". This is implemented if
"recordname" is a DATATYPE or a TYPERECORD -- all other record types
will cause an error at translation time.
----------------------------------------------------------------------
The allocation of space within a datum of a given type is not
necessarily in the order that the fields are given in the
declaration: viz.
(DATATYPE MESSAGE ((LENGTH BIT 4) (HEADER POINTER) (COUNT HALF) (DATE
BIT 12]
here, the actual representation of a MESSAGE would be:
bit: 0 17 18 21 22 33 34 35
------------------------------------------
| header |length| date |un- | word 1
| | | |used|
------------------------------------------
| count | unused | word 2
| | |
------------------------------------------
----------------------------------------------------------------------
Currently, the DATATYPE part of the record package is not included in
the file RECORD.COM but is in the file RECORDSTRUC.COM; thus after
(DATATYPE MESSAGE ((TIME REAL) (UNITS INTEGER) HEADER TRAILER)
TIME←(CLOCK 0]
you will first get the query:
Shall I load RECORD.COM? ...Yes
and then
Shall I load RECORDSTRUC.COM? ...Yes
RECORDSTRUC contains a minimal amount of interface to the record
package, but MAINLY all of the functions and macros for accessing and
storing in data-types.
----------------------------------------------------------------------
Since a DATATYPE need be allocated even when no translating
information is necessary, the RECORDS* prettymacro prints out an
ALLOCATESTRUC expression that IS copied to the .COM file upon
compilation. This is so files with DATATYPE declarations can be
compiled and loaded and run without loading the entire RECORD and
RECORDSTRUC files.
SUBRS FOR USER-DEFINED DATA TYPES
DEFTYPE[nwrds; nptrs]
Defines a new data type such that each object of that type has nwrds
words and nptrs pointers within those nwrds. If there are no
available types, an attempt is made to find a data type which has;
(1) the proper number of pointers, (2) at least the needed number of
words, and (3) has been marked as "not in use" via TYPESTATUS. The
data type number selected is returned as the value of DEFTYPE.
GETNPTRS[typen]
Returns the number of pointers in an object whose type is typen.
GETNWRDS[typen]
Returns the number of words in a object whose type is typen.
NALLOC[typen]
Allocates an object of type typen, clears its contents to zero and
returns a pointer to it.
USERCONS[typen; p1; p2; ... ; pn]
Similar to NALLOC but fills in the pointers within the object with
the arguments p1, p2, ... , pn. If too many arguments are given,
the extra ones are ignored. If too few arguments are given, NILs are
supplied.
TYPESTATUS[typen; status]
Sets the status of data type typen to status and returns the
previous status. If status is NIL, the status of typen is left
unchaged. The meaning of the status is as follows:
Status = 0 - the data type has never been used.
= 1 - the data type is currently in use. The status is set
to 1 by DEFTYPE when it selects a type.
= 2 - the type was previously in use, but is now available
for selection by DEFTYPE if no other types are available.
Actually, a status value of 1 is the only value DEFTYPE checks for.
It is advisable to set the status to 2, instead of 0, when freeing a
data type, simply to distinguish between "not in use" and "has never
been in use".
DEFEVAL[typen; fn]
Defines how the data type typen is to be evaluated. When EVAL
tries to evaluate an item of type typen, it will call fn with the
item as its argument. The result returned by fn is then the result
of the evaluation. If typen is not a valid type number, or if the
type number is that of lists, litatoms, or numbers, then DEFEVAL
simply returns NIL. If fn is T or EVAL, the evaluation is reset to
the default where the items of the given type simply evaluate to
themselves. If fn is NIL, the evaluation function is not changed,
and the current one, for that type, is returned.
For compatibility with the compiler, there is the variable
COMPILETYPELST. COMPILETYPELST is an a-list of type-number, function
pairs. When an attempt is made to compile code for an item whose
type number is on COMPILETYPELST, the item is passed to the
corresponding function. The result from the function, as with
macros, is then compiled in place of the original item. Like macros,
the function may call compiler functions itself, and return
INSTRUCTIONS.
For example, (24 . (LAMBDA(X) <'PRINT (KWOTE X)>)) will cause
unquoted strings to be compiled as a call to PRINT. The DEFEVAL
equivalent is DEFEVAL(24 PRINT).